package com.atguigu.gmall.mq.reveiver;

import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * @Author: 小兰
 * @Date: 2023/5/8 19:46
 */
@Component
@Slf4j
public class ConfirmReceiver {
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     *  业务操作天然支持幂等性:查询,删除,修改(固定修改A->B)
     * @param msg
     * @param channel
     * @param message
     */
    @SneakyThrows
    @RabbitListener(
            bindings = @QueueBinding(
                    exchange = @Exchange("exchange.confirm"),
                    value = @Queue("confirm.queue"),
                    key = "routing.confirm"
            )
    )
    public void  receiverConfirmMessage(String msg, Channel channel, Message message){
        //开启手动确认,,参数一: 投递消息,消息的标签  参数二: 是否批量确认
        //消费者由于生产者重发消息或者broker服务器本身导致消息重发
        //保证消息幂等性  某一项操作执行一次或者多次达到的结果一样
        //解决方案:通过传输业务数据找到业务唯一(流水号,ID)值作为判断是,是否第一次执行
        //判断消息是否为重复投递
        try {
            String redisKey = "mq:"+msg;
            //setIfAbsent(没有存,,true,,有是false
            Boolean flag = redisTemplate.opsForValue().setIfAbsent(redisKey, null, 1, TimeUnit.SECONDS);
            if (!flag){
                log.error("消费者重复监听消息,不做处理");
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                return;
            }
            log.info("消费者接收到的消息:{}",msg);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
        } catch (Exception e) {
            throw new RuntimeException(e);
            //进行消息重新入对,再次投递-参考生产者
            //todo 重新入对次数达到上限,通知运维人员;将消费失败的消息发送到异常队列中
            //todo 提供单独消费者,将一场任务存入到异常任务表中,人工处理
        }
    }


}
